#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SIZE 512
#define ZERO '\0'
#define SEP " "
#define NUM 32
#define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; }while(0)
#define SkipSpace(cmd, pos) do{\
    while(1){\
        if(isspace(cmd[pos]))\
            pos++;\
        else break;\
    }\
}while(0)

// "ls -a -l -n > myfile.txt"
#define None_Redir 0
#define In_Redir   1
#define Out_Redir  2
#define App_Redir  3

int redir_type = None_Redir;
char *filename = NULL;

// 为了方便，我就直接定义了
char cwd[SIZE*2];
char *gArgv[NUM];
int lastcode = 0;

void Die()
{
    exit(1);
}

const char *GetHome()
{
    const char *home = getenv("HOME");
    if(home == NULL) return "/";
    return home;
}

const char *GetUserName()
{
    const char *name = getenv("USER");
    if(name == NULL) return "None";
    return name;
}
const char *GetHostName()
{
    const char *hostname = getenv("HOSTNAME");
    if(hostname == NULL) return "None";
    return hostname;
}
// 临时
const char *GetCwd()
{
    const char *cwd = getenv("PWD");
    if(cwd == NULL) return "None";
    return cwd;
}

// commandline : output
void MakeCommandLineAndPrint()
{
    char line[SIZE];
    const char *username = GetUserName();
    const char *hostname = GetHostName();
    const char *cwd = GetCwd();

    SkipPath(cwd);
    snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, strlen(cwd) == 1 ? "/" : cwd+1);
    printf("%s", line);
    fflush(stdout);
}

int GetUserCommand(char command[], size_t n)
{
    char *s = fgets(command, n, stdin);
    if(s == NULL) return -1;
    command[strlen(command)-1] = ZERO;
    return strlen(command); 
}


void SplitCommand(char command[], size_t n)
{
    (void)n;
    // "ls -a -l -n" -> "ls" "-a" "-l" "-n" 
    gArgv[0] = strtok(command, SEP);
    int index = 1;
    while((gArgv[index++] = strtok(NULL, SEP))); // done, 故意写成=,表示先赋值，在判断. 分割之后，strtok会返回NULL，刚好让gArgv最后一个元素是NULL, 并且while判断结束
}

void ExecuteCommand()
{
    pid_t id = fork();
    if(id < 0) Die();
    else if(id == 0)
    {
        //重定向设置
        if(filename != NULL){
            if(redir_type == In_Redir)
            {
                int fd = open(filename, O_RDONLY);
                dup2(fd, 0);
            }
            else if(redir_type == Out_Redir)
            {
                int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                dup2(fd, 1);
            }
            else if(redir_type == App_Redir)
            {
                int fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
                dup2(fd, 1);
            }
            else
            {}
        }

        // child
        execvp(gArgv[0], gArgv);
        exit(errno);
    }
    else
    {
        // fahter
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if(rid > 0)
        {
            lastcode = WEXITSTATUS(status);
            if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);
        }
    }
}

void Cd()
{
    const char *path = gArgv[1];
    if(path == NULL) path = GetHome();
    // path 一定存在
    chdir(path);

    // 刷新环境变量
    char temp[SIZE*2];
    getcwd(temp, sizeof(temp));
    snprintf(cwd, sizeof(cwd), "PWD=%s", temp);
    putenv(cwd); // OK
}

int CheckBuildin()
{
    int yes = 0;
    const char *enter_cmd = gArgv[0];
    if(strcmp(enter_cmd, "cd") == 0)
    {
        yes = 1;
        Cd();
    }
    else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0)
    {
        yes = 1;
        printf("%d\n", lastcode);
        lastcode = 0;
    }
    return yes;
}

void CheckRedir(char cmd[])
{
    // > >> <
    // "ls -a -l -n >  myfile.txt"
    int pos = 0;
    int end = strlen(cmd);

    while(pos < end)
    {
        if(cmd[pos] == '>')
        {
            if(cmd[pos+1] == '>')
            {
                cmd[pos++] = 0;
                pos++;
                redir_type = App_Redir;
                SkipSpace(cmd, pos);
                filename = cmd + pos;
            }
            else
            {
                cmd[pos++] = 0;
                redir_type = Out_Redir;
                SkipSpace(cmd, pos);
                filename = cmd + pos;
            }
        }
        else if(cmd[pos] == '<')
        {
            cmd[pos++] = 0;
            redir_type = In_Redir;
            SkipSpace(cmd, pos);
            filename = cmd + pos;
        }
        else
        {
            pos++;
        }
    }
}

int main()
{
    int quit = 0;
    while(!quit)
    {
        // 0. 重置
        redir_type = None_Redir;
        filename = NULL;
        // 1. 我们需要自己输出一个命令行
        MakeCommandLineAndPrint();

        // 2. 获取用户命令字符串
        char usercommand[SIZE];
        int n = GetUserCommand(usercommand, sizeof(usercommand));
        if(n <= 0) return 1;

        // 2.1 checkredir
        CheckRedir(usercommand);

        // 2.2 debug
//        printf("cmd: %s\n", usercommand);
//        printf("redir: %d\n", redir_type);
//        printf("filename: %s\n", filename);
//
        // 3. 命令行字符串分割. 
        SplitCommand(usercommand, sizeof(usercommand));

        // 4. 检测命令是否是内建命令
        n = CheckBuildin();
        if(n) continue;
        // 5. 执行命令
        ExecuteCommand();
    }
    return 0;
}






















//#include<stdio.h>
//#include<unistd.h>
//#include<sys/types.h>
//#include<string.h>
//#include<stdlib.h>
//#include<sys/wait.h>
//#include<errno.h>
//
//#define SIZE 100
//#define COMMAND_SIZE 200
//#define ZEOR '\0'
//#define ARGV_NUM 32
//#define SKIP " "
//#define SkipPath(p) do{p+=strlen(p)-1; while(*p!='/'){p--;}}while(0)
//
//char* gArgv[ARGV_NUM];//指令参数
//char cwd[SIZE*2];//当前路径的环境变量键值对
//int lastcode=0;
//
//const char* GetUserName(){//获取环境变量中的值
//  const char* name=getenv("USER");
//  if(name==NULL){
//    return "None";
//  }
//  return name;
//}
//
//const char* GetHome(){//获取家目录
//  const char* home=getenv("HOME");
//  if(home==NULL){
//    return "/";
//  }
//  return home;
//}
//
//const char* GetHostHome(){//获取主机名
//  const char* hostname=getenv("HOSTNAME");
//  if(hostname==NULL){
//    return "None";
//  }
//  return hostname;
//}
//
//const char* GetCwd(){//获取当前路径
//  const char* cwd=getenv("PWD");
//  if(cwd==NULL){
//    return "None";
//  }
//  return cwd;
//}
//
//
////输出命令行
//void MakeCommandLineAndPrint(){
//    char line[SIZE];
//    const char* name=GetUserName();
//    const  char* hostname=GetHostHome();
//    const char* cwd=GetCwd();
//    SkipPath(cwd);
//    snprintf(line,sizeof(line),"[%s@%s %s]> ",name,hostname,cwd=strlen(cwd)==1?"/":cwd+1);
//    printf("%s",line);
//    fflush(stdout);
//}
////获取用户命令行
//int GetUserCommand(char command[],size_t n){
//  char* s=fgets(command,n,stdin);
//  if(s==NULL)return -1;
//  command[strlen(command)-1]=ZEOR;
//  return (int)strlen(command);
//}
//
//
////命令行分割,获取命令行参数列表
//void SplitCommand(char command[],size_t n){
//   gArgv[0]=strtok(command,SKIP);
//   size_t index=1;
//   char* t=gArgv[0];
//   while(t!=NULL){
//     t=strtok(NULL,SKIP);
//      gArgv[index++]=t;
//   }
//   //size_t i=0;
//   //for(;i<index;i++){
//   //  printf("%s\n",gArgv[i]);
//   //}
//}
//
////cd内建命令
//void Cd(){
//  const char* path=gArgv[1];
//  if(path==NULL){
//    path=GetHome();
//  }
//  //更新当前工作目录
//  chdir(path);//修改当前进程的工作路径
//
//  //更新环境变量
//  char t[SIZE*2];
//  getcwd(t,sizeof(t));
//  snprintf(cwd,sizeof(cwd),"PWD=%s",t);
//  putenv(cwd);
//}
//
////查看是否是内建命令
//int CheckBuiltIn(){
//  int yes=0;
//  const char* cmd=gArgv[0];
//  if(strcmp(cmd,"cd")==0){
//    yes=1;
//    Cd();
//  }
//  return yes;
//}
//// 处理创建子进程失败
//void Die(){
//  exit(1);
//}
////执行指令
//void ExeCommand(){ 
//   pid_t id=fork();
//   if(id<0){
//     Die();
//   }
//   if(id==0){
//     //child
//     execvp(gArgv[0],gArgv);
//     exit(errno);
//   }else{
//     int status=0;
//     pid_t res=waitpid(id,&status,0);
//     if(res>0){
//       lastcode=WEXITSTATUS(status);//获取子进程退出码信息
//       if(WIFEXITED(status)){
//        if(lastcode!=0) printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);
//       }
//     }
//   }
//}
//
//int main(){
//  int quit=0;
//  while(!quit){
//     //1.输出一个命令行
//     MakeCommandLineAndPrint();
//    
//     //2.获取用户命令
//     char command[COMMAND_SIZE];
//     int n= GetUserCommand(command,sizeof(command));
//     if(n<=0)return 1;//输入指令不合法
//    // printf("%s\n",command);
//
//    //3.命令行字符串分割
//     SplitCommand(command,sizeof(command));
//    
//     //4.查看命令是否是内建命令
//     int flag=CheckBuiltIn();
//     if(flag)continue;
//     
//     //5.执行指令
//     ExeCommand();
//  }
//  return 0;
//}
//
